--Finite State Machine 
--有限状态机

local SEPARATOR = '.'
local ANY       = '*'	--任意状状
local ANYSTATE  = ANY .. SEPARATOR
local ANYEVENT  = SEPARATOR .. ANY
local UNKNOWN   = ANYSTATE .. ANY

local FSM = {}

function FSM.new(...)
    local instance = setmetatable({}, {__index=FSM})
    instance:ctor(...)
    return instance
end

--tbState 状态表
function FSM:ctor(tbState)
    self.state = tbState[1][1]	-- 第一个设为默认状态
    self.stt = {}			-- 事件表 包含 当前状态-要到的状态-回调
    self.str = ""			-- <self.state><SEPARATOR><event> combination
    self.silence = false	-- 默认状态打印错误

    --初始化为第一个状态
    self.stt[UNKNOWN] = {
        newState = self.state,
        action = function(...) FSM.exception(self) end
    }
    self:add(tbState)
end

--设置当前状态
function FSM:set(s)
    self.state = s
end

--当前状态
function FSM:get()
    return self.state
end

--
function FSM:silent()
    self.silence = true --不输出错误
end

-- 把异常抛出
function FSM:exception()
    if self.silence == false then
        print("FSM: unknown combination: " .. self.str)
    end
    return false
end

-- 从当前状态切换到事件对应状态
function FSM:fire(event)
    local act = self.stt[self.state .. SEPARATOR .. event]
    if act==nil then
        act = self.stt[ANYSTATE .. event]
        if act==nil then
            act = self.stt[self.state .. ANYEVENT]
            if act==nil then
                act = self.stt[UNKNOWN];
                self.str = self.state .. SEPARATOR .. event
            end
        end
    end
    self.state = act.newState
    return act.action()
end

-- 添加一个状态表
function FSM:add(t)
    for _,v in ipairs(t) do
        local oldState, event, newState, action = v[1], v[2], v[3], v[4]
        self.stt[oldState .. SEPARATOR .. event] = {newState = newState, action = action}
    end
    return #t	-- the requested number of self.state-transitions to be added
end

-- remove self.state transitions from the FSM
function FSM:delete(t)
    for _,v in ipairs(t) do
        local oldState, event = v[1], v[2]
        if oldState == ANY and event == ANY then
            if not self.silence then
                print( "FSM: you should not delete the exception handler" )
                print( "FSM: but assign another exception action" )
            end
            -- assign default exception handler but stay in current self.state
            --self.stt[exception] = {newState = self.state, action = exception}
        else
            self.stt[oldState .. SEPARATOR .. event] = nil
        end
    end
    return #t 	-- the requested number of self.state-transitions to be deleted
end

------------------------------------------------------------------
--测试
------------------------------------------------------------------
if arg and arg[0] == "fsm.lua" then
    local function sleep(n)
        if type(n) ~= "number" then
            return
        end
        local t0 = os.clock()
        while os.clock() - t0 <= n do

        end
    end


    local Test = {}
    function Test.new(...)
        local instance = setmetatable({}, {__index=Test})
        instance:ctor(...)
        return instance
    end
    function Test:ctor()
        --状态
        --state1 --eventName --state2 --callback
        self.stateTable = {
            {"rest", 	"to_start", 	"start", 	function() self:onStart() end },
            {"start", 	"to_bet",		"bet", 		function() self:onBet() end },
            {"bet", 	"to_open",		"open", 	function() self:onOpen() end },
            {"*", 		"to_global",	"global", 	function() self:onGlobal() end },
            {"global", 	"leave_global",	"*", 		function() self:onLeaveGlobal() end },
            {"*", 		"to_rest",		"rest", 	function() self:onRest() end },
        }
        --状态机
        self.fsm = FSM.new(self.stateTable)
        -- self.fsm:set("rest")
        self.fsm:fire("to_start")
    end
    function Test:onStart()
        print("______________start__")
        sleep(1)
        self.fsm:fire("to_bet")
    end
    function Test:onBet()
        print("______________bet__")
        sleep(1)
        self.fsm:fire("to_open")
    end
    function Test:onOpen()
        print("______________open__")
        sleep(1)
        self.fsm:fire("to_global")
    end
    function Test:onGlobal( ... )
        print("___________global___")
        sleep(1)
        self.fsm:fire("leave_global")
    end
    function Test:onLeaveGlobal( ... )
        print("__________leave_global___")
        print("currut state name:", self.fsm:get())
        sleep(1)
        self.fsm:fire("to_rest")
    end
    function Test:onRest()
        print("______________rest__")
        --sleep(5)
        --self.fsm:fire("to_start")
    end


    local test = Test.new()


end


return FSM